package com.googlecode.ddlutilstool.kernel;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.Set;

import com.googlecode.ddlutilstool.common.DriverBean;
import com.googlecode.ddlutilstool.common.INIProperties;
import com.googlecode.ddlutilstool.common.ProfileBean;

/**
 * <p>Class ProfileManager</p>
 * 
 * <p>This is a Singleton class that manages all file 
 * access to the profiles and drivers.</p>
 * 
 * <p>At startup, it will load the profiles.ini and drivers.ini
 * files from the current directory.  If either are missing, it
 * will create default ones.</p>
 * 
 * <p>Whenever a profile or driver needs to be loaded or saved,
 * it is done through method in this class.</p>
 */
public class ProfileManager {

	public static final String PROFILE_FILENAME = "profiles.ini";
	public static final String DRIVERS_FILENAME = "drivers.ini";
	
	public static final String DEFAULT_PROFILE_NAME = "New Profile";

	public static final String KEY_PROFILE_DRIVER = "driver";
	public static final String KEY_PROFILE_URL = "url";
	public static final String KEY_PROFILE_USER = "user";
	public static final String KEY_PROFILE_PASSWORD = "password";
	public static final String KEY_PROFILE_SCHEMA = "schema";

	public static final String DEFAULT_DRIVER_NAME = "New Driver";
	
	public static final String KEY_DRIVER_CLASSNAME = "classname";
	public static final String KEY_DRIVER_URLHINT = "urlhint";

	private static ProfileManager mInstance = null;
	
	public static ProfileManager getInstance() {
		if (mInstance==null) {
			mInstance = new ProfileManager();
		}
		return mInstance;
	}

	private INIProperties mProfiles;
	private INIProperties mDrivers;
	
	private ProfileManager() {
	}

	/**
	 * reads in, or creates the profiles and drivers.
	 * It stores the current list of each in memory.
	 * @throws IOException
	 */
	void init() throws IOException {
		File f = new File(PROFILE_FILENAME);
		if (f.exists()) {
			FileReader reader = null;
			try {
				reader = new FileReader(f);
				mProfiles = INIProperties.read(reader);
			}
			finally {
				if (reader!=null) try {
					reader.close();
				}
				catch (IOException e) {
				}
			}
		}
		else {
			mProfiles = new INIProperties();
		}
		f = new File(DRIVERS_FILENAME);
		if (f.exists()) {
			FileReader reader = null;
			try {
				reader = new FileReader(f);
				mDrivers = INIProperties.read(reader);
			}
			finally {
				if (reader!=null) try {
					reader.close();
				}
				catch (IOException e) {
				}
			}
		}
		else {
			mDrivers = new INIProperties();
		}
	}
	
	/**
	 * @return the full list of profile names from the profiles.ini file.
	 */
	public Set<String> getProfileNames() {
		return mProfiles.getGroupNames();
	}
	
	/**
	 * @return the full list of driver names from the drivers.ini file.
	 */
	public Set<String> getDriverNames() {
		return mDrivers.getGroupNames();
	}
	
	/**
	 * Gets the profile from the INI file.
	 * 
	 * @param pName
	 * @return
	 */
	public ProfileBean getProfile(String pName) {
		Map<String,String> props = mProfiles.getGroup(pName);
		if (props!=null) {
			ProfileBean p = new ProfileBean();
			p.setName(pName);
			p.setDriver(props.get(KEY_PROFILE_DRIVER));
			p.setUrl(props.get(KEY_PROFILE_URL));
			p.setUser(props.get(KEY_PROFILE_USER));
			p.setPassword(props.get(KEY_PROFILE_PASSWORD));
			p.setSchema(props.get(KEY_PROFILE_SCHEMA));
			return p;
		}
		return null;
	}
	
	/**
	 * Writes the profile to the profiles.ini file.
	 * It will update it if it already exists, or add
	 * it if it doesn not.
	 * @param pProfile the profile details.
	 * @throws IOException
	 */
	public void setProfile(ProfileBean pProfile) throws IOException {
		mProfiles.setProperty(pProfile.getName(), KEY_PROFILE_DRIVER, pProfile.getDriver());
		mProfiles.setProperty(pProfile.getName(), KEY_PROFILE_URL, pProfile.getUrl());
		mProfiles.setProperty(pProfile.getName(), KEY_PROFILE_USER, pProfile.getUser());
		mProfiles.setProperty(pProfile.getName(), KEY_PROFILE_PASSWORD, pProfile.getPassword());
		mProfiles.setProperty(pProfile.getName(), KEY_PROFILE_SCHEMA, pProfile.getSchema());
		
		writeProfiles();
	}
	
	/**
	 * Deletes a profile from the profiles.ini file.
	 * @param pName the name of the profile
	 * @throws IOException
	 */
	public void removeProfile(String pName) throws IOException {
		mProfiles.removeGroup(pName);
		writeProfiles();
	}
	
	/**
	 * Called internally, updates the profiles.ini file with
	 * the contents from memory.
	 * @throws IOException
	 */
	protected void writeProfiles() throws IOException {
		File f = new File(PROFILE_FILENAME);
		FileWriter writer = null;
		try {
			writer = new FileWriter(f);
			INIProperties.write(mProfiles,writer);
		}
		finally {
			if (writer!=null) try {
				writer.close();
			}
			catch (IOException e) {
			}
		}
	}

	/**
	 * Gets the driver from the INI file.
	 * 
	 * @param pName
	 * @return
	 */
	public DriverBean getDriver(String pName) {
		Map<String,String> props = mDrivers.getGroup(pName);
		if (props!=null) {
			DriverBean d = new DriverBean();
			d.setName(pName);
			d.setClassname(props.get(KEY_DRIVER_CLASSNAME));
			d.setUrlHint(props.get(KEY_DRIVER_URLHINT));
			return d;
		}
		return null;
	}
	
	/**
	 * Writes the driver to the drivers.ini file.
	 * If it exists already, it will be updated.  If it
	 * does not, it will be added.
	 * 
	 * @param pDriver the profile details.
	 * @throws IOException
	 */
	public void setDriver(DriverBean pDriver) throws IOException {
		mDrivers.setProperty(pDriver.getName(), KEY_DRIVER_CLASSNAME, pDriver.getClassname());
		mDrivers.setProperty(pDriver.getName(), KEY_DRIVER_URLHINT, pDriver.getUrlHint());
		
		writeDrivers();
	}
	
	/**
	 * Removes the profile from the drivers.ini file.
	 * 
	 * @param pName the name of the driver
	 * @throws IOException
	 */
	public void removeDriver(String pName) throws IOException {
		mDrivers.removeGroup(pName);
		writeDrivers();
	}
	
	/**
	 * Called internally, it updates the drivers.ini file
	 * with the contents from memory.
	 * 
	 * @throws IOException
	 */
	protected void writeDrivers() throws IOException {
		File f = new File(DRIVERS_FILENAME);
		FileWriter writer = null;
		try {
			writer = new FileWriter(f);
			INIProperties.write(mDrivers,writer);
		}
		finally {
			if (writer!=null) try {
				writer.close();
			}
			catch (IOException e) {
			}
		}
	}

	/**
	 * Used to fill the Profile dialog when the
	 * user chooses to create a new profile, it fills
	 * in the default fields.  It will just fill in
	 * the name with a unique one.
	 * @return the profile to use in the Profile dialog.
	 */
	public ProfileBean createDefaultProfile() {
		int count=0;
		
		String name=null;
		while (name==null) {
			if (count>0) {
				name=DEFAULT_PROFILE_NAME+" ("+count+")";
			}
			else {
				name=DEFAULT_PROFILE_NAME;
			}
			if (getProfile(name)!=null) {
				name=null;
			}
			count++;
		}
		ProfileBean profile = new ProfileBean();
		profile.setName(name);

		Set<String> driverNames = mDrivers.getGroupNames();
		if (driverNames.size()>0) {
			String driverName = driverNames.iterator().next();
			DriverBean driver = getDriverByName(driverName);
			profile.setDriver(driver.getClassname());
			profile.setUrl(driver.getUrlHint());
		}
		return profile;
	}
	/**
	 * Used to fill the Drivers dialog when the
	 * user chooses to create a new driver, it fills
	 * in the default fields.  It will just fill in
	 * the name with a unique one.
	 * @return the profile to use in the Drivers dialog.
	 */
	public DriverBean createDefaultDriver() {
		int count=0;
		
		String name=null;
		while (name==null) {
			if (count>0) {
				name=DEFAULT_DRIVER_NAME+" ("+count+")";
			}
			else {
				name=DEFAULT_DRIVER_NAME;
			}
			if (getDriver(name)!=null) {
				name=null;
			}
		}
		DriverBean driver = new DriverBean();
		driver.setName(name);

		return driver;
	}
	/**
	 * Gets the driver from the INI file.
	 * 
	 * @param pName
	 * @return
	 */
	public DriverBean getDriverByName(String pName) {
		Map<String,String> props = mDrivers.getGroup(pName);
		if (props!=null) {
			DriverBean driver = new DriverBean();
			driver.setName(pName);
			driver.setClassname(props.get(KEY_DRIVER_CLASSNAME));
			driver.setUrlHint(props.get(KEY_DRIVER_URLHINT));
			return driver;
		}
		return null;
	}
	
	/**
	 * Locates the driver from the drivers.ini file by
	 * the jdbc driver classname.
	 * @param pClassName the classname to match
	 * @return the driver bean that matches, or null
	 */
	public DriverBean getDriverByClass(String pClassName) {
		for (String group : mDrivers.getGroupNames()) {
			String className = mDrivers.getProperty(group, KEY_DRIVER_CLASSNAME);
			if (className!=null && className.equals(pClassName)) {
				return  getDriverByName(group);
			}
		}
		return null;
	}
}
